/********************************************
 * Copyrigt (C) rumi
 * Module: app_serial.cpp
 *********************************************/
#include "app_serial.hpp"
#include "ser_global_cfg.h"
#include <iostream>
#include <thread>
#include <mutex>
#include <string>
#include <chrono>
#include <queue>
#include <condition_variable>
#include <cstring>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include "ser_system.h"
#include <iomanip> 
#include <sstream> 
#include <sys/mman.h>
#include <sys/stat.h>
#include <semaphore.h>
#include <sys/types.h>
#include <cstdlib>
#include <ctime>

E_SYS_STATE s_Auto_SystemState = E_SYS_STATE_INIT;

std::mutex Mtx_Uart;
std::condition_variable ConSig_Uart;
std::queue<std::string> messageQueue;

std::string portName = AUTO_UART_NAME; 
static int baudRate = AUTO_UART_SPEED;

static uint8_t s_Send_ThreadCreatState = 0;
static uint8_t s_Recv_ThreadCreatState = 0;
static uint8_t s_ThreadCreatState = 0;
SharedMemory_TypeDef *SharedMemoryRecv;
SharedMemory_TypeDef *SharedMemorySend;


bool stopThreads = false; // New: flag to stop threads

int setupSerialPort(const std::string &portName, int baudRate);
void AutoUart_ReceiveThread(int serialPort);
void AutoUart_SendThread(int serialPort);
void APP_Auto_CreateThread();

void APP_Auto_CreateThread() {
    s_ThreadCreatState = 1;
    int serialPort = setupSerialPort(portName, baudRate);
    if (serialPort < 0) 
    {
        printf("Error: Unable to open serial port.\n");
        return;
    }
    std::thread Auto_UartReceiver(AutoUart_ReceiveThread, serialPort);
    std::thread Auto_UartSender(AutoUart_SendThread, serialPort);

    Auto_UartReceiver.detach();
    Auto_UartSender.detach();
}

void APP_Auto_System_Cycle() {
    static uint8_t timer = 0;

    switch (s_Auto_SystemState) {
        case E_SYS_STATE_INIT: {
            
            if (s_ThreadCreatState == 0)
            {
                APP_Auto_CreateThread();
            }
            if (s_Send_ThreadCreatState == 1 && s_Recv_ThreadCreatState == 1) {
                s_Auto_SystemState = E_SYS_STATE_ENTRY_STANDBY;
                printf("Auto Entry Standby\n");
            }
            break;
        }

        case E_SYS_STATE_ENTRY_STANDBY: {
            s_Auto_SystemState = E_SYS_STATE_ACTIVE_STANDBY;
            printf("Auto Active Standby\n");
            break;
        }

        case E_SYS_STATE_ACTIVE_STANDBY: {
            s_Auto_SystemState = E_SYS_STATE_EXIT_STANDBY;
            printf("Auto Exit Standby\n");
            break;
        }

        case E_SYS_STATE_EXIT_STANDBY: {
            s_Auto_SystemState = E_SYS_STATE_ENTRY_NORMAL;
            printf("Auto Entry Normal\n");
            break;
        }

        case E_SYS_STATE_ENTRY_NORMAL: {
            s_Auto_SystemState = E_SYS_STATE_ACTIVE_NORMAL;
            printf("Auto Active Normal\n");
            break;
        }

        case E_SYS_STATE_ACTIVE_NORMAL: {
            timer++;
            if (timer >= 200) { 
                timer = 0;
                std::lock_guard<std::mutex> lock(Mtx_Uart);
                if (messageQueue.empty()) {
                    messageQueue.push("heartbeat");
                }
                ConSig_Uart.notify_one();
            }
            break;
        }

        default:
            break;
    }
}

int setupSerialPort(const std::string &portName, int baudRate) {
    int serialPort = open(portName.c_str(), O_RDWR | O_NOCTTY | O_SYNC);
    if (serialPort < 0) {
        std::cerr << "Error: Unable to open serial port." << std::endl;
        return -1;
    }

    struct termios tty;
    memset(&tty, 0, sizeof tty);
    if (tcgetattr(serialPort, &tty) != 0) {
        std::cerr << "Error: Unable to get terminal attributes." << std::endl;
        close(serialPort);
        return -1;
    }

    speed_t baud;
    switch (baudRate) {
        case 9600: baud = B9600; break;
        case 19200: baud = B19200; break;
        case 115200: baud = B115200; break;
        default:
            std::cerr << "Error: Unsupported baud rate." << std::endl;
            close(serialPort);
            return -1;
    }

    cfsetospeed(&tty, baud);
    cfsetispeed(&tty, baud);

    tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;
    tty.c_iflag &= ~IGNBRK;
    tty.c_lflag = 0;
    tty.c_oflag = 0;
    tty.c_cc[VMIN] = 1;
    tty.c_cc[VTIME] = 1;

    tty.c_iflag &= ~(IXON | IXOFF | IXANY);
    tty.c_cflag |= (CLOCAL | CREAD);
    tty.c_cflag &= ~(PARENB | PARODD);
    tty.c_cflag &= ~CSTOPB;
    tty.c_cflag &= ~CRTSCTS;

    if (tcsetattr(serialPort, TCSANOW, &tty) != 0) {
        std::cerr << "Error: Unable to set terminal attributes." << std::endl;
        close(serialPort);
        return -1;
    }

    return serialPort;
}

void AutoUart_ReceiveThread(int serialPort) {
    char buffer[64];
    s_Recv_ThreadCreatState = 1;
    int SharedMem_Recvfd = shm_open(SHM_NAME_RECV, O_CREAT | O_RDWR, 0666);
    if (SharedMem_Recvfd == -1) {
        std::cerr << "Error: Unable to open shared memory for writing." << std::endl;
        return;
    }
    ftruncate(SharedMem_Recvfd, sizeof(SharedMemory_TypeDef));
    SharedMemoryRecv = (SharedMemory_TypeDef *)mmap(nullptr, sizeof(SharedMemory_TypeDef), PROT_WRITE, MAP_SHARED, SharedMem_Recvfd, 0);
    if (SharedMemoryRecv == MAP_FAILED) {
        std::cerr << "Error: Unable to map shared memory for writing." << std::endl;
        close(SharedMem_Recvfd);
        return;
    }

    std::cout << "Shared memory address in app: " << SharedMemoryRecv << std::endl;
    std::cout << "Shared memory data: " << SharedMemoryRecv->Data << std::endl;

    if(sem_init(&SharedMemoryRecv->Sem_Notice, 1, 0) == -1)
    {
        std::cerr << "Error: Unable to init semaphore." << std::endl;
    }
    SharedMemoryRecv->DataState = DataState_Recvd;

    while (!stopThreads) {
        ssize_t bytesRead = read(serialPort, buffer, sizeof(buffer) - 1);
        if (bytesRead > 0) {

            auto now = std::chrono::system_clock::now();
            std::time_t now_time = std::chrono::system_clock::to_time_t(now);
            auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) % 1000;
            std::ostringstream timestampStream;
            timestampStream << std::put_time(std::localtime(&now_time), "%Y-%m-%d %H:%M:%S") << "." << std::setfill('0') << std::setw(3) << milliseconds.count();
            std::string timestamp = timestampStream.str();
            std::string receivedData = "[" + timestamp + "] " + buffer;
            std::lock_guard<std::mutex> lock(Mtx_Uart);
            std::cout << "Received: " << receivedData << std::endl;

            printf("DataState: %d\n", SharedMemoryRecv->DataState);
            if(SharedMemoryRecv->DataState == DataState_Recvd)
            {               
                // copy data to shared memory
                printf("copy data to shared memory\n");
                SharedMemoryRecv->DataState = DataState_Working;
                strncpy(SharedMemoryRecv->Data, buffer, sizeof(buffer));
                sem_post(&SharedMemoryRecv->Sem_Notice);
            }
        }
    }

    munmap(SharedMemoryRecv, sizeof(SharedMemory_TypeDef));
    close(SharedMem_Recvfd);
    shm_unlink(SHM_NAME_RECV);
    close(serialPort);
}

void AutoUart_SendThread(int serialPort) {
    s_Send_ThreadCreatState = 1;
    static char aSharedSendData[64];
    static uint8_t *pSharedSendData;

    int SharedMem_Sendfd = shm_open(SHM_NAME_SEND, O_CREAT | O_RDWR, 0666);
    if (SharedMem_Sendfd == -1) {
        std::cerr << "Error: Unable to open shared memory for writing." << std::endl;
        return;
    }
    ftruncate(SharedMem_Sendfd, sizeof(SharedMemory_TypeDef));
    SharedMemorySend = (SharedMemory_TypeDef *)mmap(0, sizeof(SharedMemory_TypeDef), PROT_WRITE, MAP_SHARED, SharedMem_Sendfd, 0);
    if (SharedMemorySend == MAP_FAILED) {
        std::cerr << "Error: Unable to map shared memory for writing." << std::endl;
        close(SharedMem_Sendfd);
        return;
    }
    if(sem_init(&SharedMemorySend->Sem_Notice, 1, 0) == -1)
    {
        std::cerr << "Error: Unable to init semaphore." << std::endl;   
    }

    while (!stopThreads) {

        if(!messageQueue.empty())
        {
            std::unique_lock<std::mutex> lock(Mtx_Uart);
            std::string message = messageQueue.front();
            messageQueue.pop();
            lock.unlock();

            ssize_t bytesWritten = write(serialPort, message.c_str(), message.size());
            if (bytesWritten < 0) {
                std::cerr << "Error: Unable to write to serial port." << std::endl;
            } else {
                std::cout << "Sent: " << message << std::endl;
            }
        }

        if(sem_trywait(&SharedMemorySend->Sem_Notice) == 1)
        {           
            // copy data from shared memory
            strncpy(aSharedSendData, SharedMemorySend->Data, sizeof(SharedMemorySend->Data));
            SharedMemorySend->DataState = DataState_Recvd;
            ssize_t bytesWritten = write(serialPort, aSharedSendData, sizeof(aSharedSendData));
            if (bytesWritten < 0) {
                std::cerr << "Error: Unable to write to serial port." << std::endl;
            } else {
                std::cout << "Sent: " << aSharedSendData << std::endl;
            }

        }
        if (stopThreads) break;
        std::this_thread::sleep_for(std::chrono::milliseconds(5));  // rest for 5ms
    }

    munmap(SharedMemorySend, sizeof(SharedMemory_TypeDef));
    close(SharedMem_Sendfd);
    shm_unlink(SHM_NAME_SEND);
    close(serialPort);
}


/********************************************
 * End of file
 *********************************************/
